package net.lgy.aspect;


import cn.hutool.jwt.JWT;
import io.jsonwebtoken.Claims;
import jakarta.servlet.http.HttpServletRequest;
import lombok.extern.slf4j.Slf4j;
import net.lgy.annotation.ShareCodeCheck;
import net.lgy.enums.BizCodeEnum;
import net.lgy.exception.BizException;
import net.lgy.util.JsonData;
import net.lgy.util.JwtUtil;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

/**
 * 分享码校验切面类
 * 用于拦截带有@ShareCodeCheck注解的方法，验证分享链接的合法性
 * 通过解析请求头中的share-token，校验分享码有效性并提取分享ID供后续业务使用
 */
@Aspect  // 标识此类为AOP切面类
@Component  // 注册为Spring组件
@Slf4j  // 提供日志输出能力
public class ShareCodeAspect {

    /**
     * 线程本地变量，用于存储当前请求的分享ID
     * 解决多线程环境下分享ID的线程安全问题
     */
    private static final ThreadLocal<Long> threadLocal = new ThreadLocal<>();

    /**
     * 设置当前线程共享的分享ID
     * @param shareId 分享ID
     */
    public static void setShareId(Long shareId) {
        threadLocal.set(shareId);
    }

    /**
     * 获取当前线程绑定的分享ID
     * @return 分享ID
     */
    public static Long getShareId() {
        return threadLocal.get();
    }

    /**
     * 定义切入点：拦截所有标注了@ShareCodeCheck注解的方法
     * @param shareCodeCheck 注解实例，用于获取注解属性（当前未使用）
     */
    @Pointcut("@annotation(shareCodeCheck)")
    public void pointCutShareCodeCheck(ShareCodeCheck shareCodeCheck) {
        // 切入点定义，方法体为空
    }

    /**
     * 环绕通知：在目标方法执行前后进行分享码校验操作
     * 1. 从请求头获取share-token
     * 2. 校验token合法性并解析出分享ID
     * 3. 将分享ID存入线程本地变量
     * 4. 执行目标方法
     * 5. 返回目标方法执行结果
     *
     * @param joinPoint 连接点对象，用于调用目标方法
     * @param shareCodeCheck 注解实例
     * @return 目标方法的返回结果
     * @throws Throwable 目标方法可能抛出的异常
     */
    @Around("pointCutShareCodeCheck(shareCodeCheck)")
    public Object around(ProceedingJoinPoint joinPoint, ShareCodeCheck shareCodeCheck) throws Throwable {
        // 1. 获取当前请求对象
        HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();

        // 2. 从请求头中获取share-token
        String header = request.getHeader("share-token");
        if (header == null) {
            // 若token不存在，抛出非法分享码异常
            throw new BizException(BizCodeEnum.SHARE_CODE_ILLEGAL);
        }

        // 3. 校验并解析JWT令牌
        Claims claims = JwtUtil.checkShareJWT(header);
        if (claims == null) {
            log.error("分享码解析失败:{}", claims);
            // 解析失败，返回非法分享码错误结果
            return JsonData.buildResult(BizCodeEnum.SHARE_CODE_ILLEGAL);
        }

        // 4. 从解析结果中提取分享ID并存储到线程本地变量
        Long shareId = Long.valueOf(claims.get(JwtUtil.CLAIM_SHARE_KEY) + "");
        setShareId(shareId);

        log.info("环绕通知执行前，shareId:{}", shareId);

        // 5. 执行目标方法
        Object proceed = joinPoint.proceed();

        log.info("环绕通知执行后，shareId:{}", shareId);

        // 6. 返回目标方法执行结果
        return proceed;
    }
}
